#include "keypad_engine.h"

#ifndef NULL
#define NULL    ((void *)0)
#endif

#ifndef TRUE
#define TRUE    1
#endif

#ifndef FALSE
#define FALSE    0
#endif


typedef uint32_t    keypad_type_t;

#define EVENT_SET_AND_EXEC_CB(key, event)    \
    do                                       \
    {                                        \
        key->event = event;                  \
        if (key->callback)                   \
            key->callback((keypad_t *)key);  \
    } while (0)


#define KEYPAD_IS_PRESSED(i)  (s_key_status_reg & (1U << i))


static volatile keypad_type_t s_key_status_reg = (keypad_type_t)0;

static volatile keypad_type_t s_logic_level = (keypad_type_t)0;

static keypad_t *s_key_head = NULL;

static volatile uint8_t s_key_count = 0;

/***************************************************************************************************
 * @fn      keypad_register
 *
 * @brief   注册按键
 *
 * @param   key 按键结构体指针
 *
 * @return  已经注册的按键个数 
 */
int32_t keypad_register(keypad_t *key)
{
    keypad_t *curr_key = s_key_head;

    /* 输入的参数为空，或者超出最大支持的按键，则退出 */
    if ((key == NULL) || (s_key_count > (sizeof(keypad_type_t) * 8)))
    {
        return -1;
    }

    /* 如果已经注册过，则退出 */
    while (curr_key)
    {
        if (curr_key == key)
        {
            return -1;
        }
        curr_key = curr_key->next;
    }

    /* 第一个注册的的按键放在链表的末尾，
     * s_key_head指向链表的头部 */
    key->next       = s_key_head;
    key->stage      = KEYPAD_STAGE_DEFAULT;
    key->scan_count = 0;
    key->multi_click_down_count = 0;
    key->short_press_flag = FALSE;
    key->multi_clicks = 0;

    #if KEYPAD_USE_LONG_HOLD
    key->long_hold_out_count = 0;
    #endif  /* end oif #if KEYPAD_USE_LONG_HOLD */
    
    s_key_head = key;

    s_key_count++;

    return s_key_count;
}

/***************************************************************************************************
 * @fn      keypad_read
 *
 * @brief   读取按键电平状态
 *
 * @param   无
 *
 * @return  无 
 */
static void keypad_read(void)
{
    uint8_t i;
    keypad_t *target;

    /* 首先注册的按钮，按钮值位于raw_data的低位 */
    keypad_type_t raw_data = 0;

    /* 遍历按键 */
    for ((target = s_key_head, i = s_key_count - 1); 
         ((target != NULL) && (target->key_pin_read != NULL));
         (target = target->next, i--))
    {
        raw_data = raw_data | ((target->key_pin_read)(target) << i);
    }

    /* 保存按键状态到 s_key_status_reg 这个变量 */
    s_key_status_reg = raw_data;
}

/***************************************************************************************************
 * @fn      keypad_process
 *
 * @brief   处理按键
 *
 * @param   无
 *
 * @return  已经注册的按键个数 
 */
static uint8_t keypad_process(void)
{
    uint8_t i;
    uint8_t active_key_count = 0;
    keypad_event_e event = KEYPAD_EVENT_NONE; 
    keypad_t *target;

    /* 遍历按键 */
    for ((target = s_key_head, i = s_key_count - 1); 
         (target != NULL);
         (target = target->next, i--))
    {
        if (target->stage == KEYPAD_STAGE_DEFAULT)
        {
            if (KEYPAD_IS_PRESSED(i))
            {
                target->scan_count++;
                target->stage = KEYPAD_STAGE_DEBOUNCE; /* 消抖判断 */
            }
        }
        else if (target->stage == KEYPAD_STAGE_DEBOUNCE)
        {
            if (KEYPAD_IS_PRESSED(i))
            {
                target->scan_count++;

                if (target->scan_count >= target->debounce_time)
                {
                    target->stage = KEYPAD_STAGE_SHORT;  /* 短按判断 */
                }
            }
            else
            {
                if (target->scan_count)
                {
                    target->scan_count--;
                }
                else
                {
                    target->stage = KEYPAD_STAGE_DEFAULT;
                }
            }
        }
        else if (target->stage == KEYPAD_STAGE_SHORT)
        {
            if (KEYPAD_IS_PRESSED(i))
            {
                target->scan_count++;

                if (target->scan_count >= target->long_press_time)
                {
                    target->stage = KEYPAD_STAGE_LONG;  /* 短按判断 */
                }
            }
            else
            {
                /* 上一次有按键按下，并且这次按下没有超时 */
                if (target->short_press_flag == TRUE && 
                    target->multi_click_down_count >= 3)
                {
                    target->multi_clicks++;
                    // target->short_press_flag = FALSE;
                    target->scan_count = 0;
                    target->multi_click_down_count = 0;
                }
                else  /* 上一次没有按键按下 */
                {
                    target->short_press_flag = TRUE;  /* 先标记发生了短按 */
                    target->scan_count = 0;
                    target->multi_click_down_count = target->multi_click_down_timeout;
                    target->stage = KEYPAD_STAGE_DEFAULT;
                }
            }
        }
        else if (target->stage == KEYPAD_STAGE_LONG)
        {
            if (KEYPAD_IS_PRESSED(i))
            {
                target->short_press_flag = FALSE;
                target->multi_click_down_count = 0;

                if (target->scan_count == target->long_press_time + 1)
                {
                    event = KEYPAD_EVENT_LONG_PRESS;
                    EVENT_SET_AND_EXEC_CB(target, event);
                }

                target->scan_count++;

                #if KEYPAD_USE_LONG_HOLD
                target->long_hold_out_count++;
                if (target->long_hold_out_count > target->long_hold_out_time)
                {
                    target->long_hold_out_count = 0;

                    event = KEYPAD_EVENT_LONG_HOLD;
                    EVENT_SET_AND_EXEC_CB(target, event);
                }
                #endif  /* end oif #if KEYPAD_USE_LONG_HOLD */
            }
            else
            {
                target->short_press_flag = FALSE;
                target->multi_click_down_count = 0;
                target->scan_count = 0;
                target->stage = KEYPAD_STAGE_DEFAULT;

                #if KEYPAD_USE_LONG_HOLD
                target->long_hold_out_count = 0;
                #endif  /* end oif #if KEYPAD_USE_LONG_HOLD */

                event = KEYPAD_EVENT_LONG_UP;
                EVENT_SET_AND_EXEC_CB(target, event);
            }
        }

        /* 已经发生了短按 */
        if (target->short_press_flag == TRUE)
        {
            /* 超过一定时间没有按键再次按下 */
            if (target->multi_click_down_count > 0)
            {
                if (target->multi_click_down_count == 1)
                {
                    target->multi_clicks++;
                    target->short_press_flag = FALSE;

                    if (target->multi_clicks > 1)
                    {
                        event = KEYPAD_EVENT_MULTI_CLICK;
                        EVENT_SET_AND_EXEC_CB(target, event);

                        target->multi_clicks = 0;

                        target->short_press_flag = FALSE;
                    }
                    else
                    {
                        /* 触发短按事件 */
                        event = KEYPAD_EVENT_SHORT_PRESS;
                        EVENT_SET_AND_EXEC_CB(target, event);

                        target->multi_clicks = 0;

                        target->short_press_flag = FALSE;
                    }                    
                }

                target->multi_click_down_count--;
            }
        }

        active_key_count++;

        if (active_key_count)
            ;
    }

    return active_key_count;    
}

/***************************************************************************************************
 * @fn      keypad_event_read
 *
 * @brief   读取按键事件
 *
 * @param   key 按键结构体指针
 *
 * @return  已经注册的按键个数 
 */
keypad_event_e keypad_event_read(keypad_t *key)
{
    return (keypad_event_e)(key->event);
}

/***************************************************************************************************
 * @fn      keypad_scan
 *
 * @brief   按键扫描函数，一般在定时中断中调用
 *
 * @param   无
 *
 * @return  已经注册的按键个数 
 */
uint8_t keypad_scan(void)
{
    keypad_read();
    return keypad_process();
}
